﻿// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System.Numerics;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.UI;
using Microsoft.UI.Input;
using System.Text;
using System.Text.RegularExpressions;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace SerialCommand.Lark
{
    enum DeviceType
    {
        LegExtension = 1, // 膝关节伸展
        LegCurl, //膝关节屈曲
        Abdominal,//腹肌训练
        BackExtension,//背肌训练
        ChestPress,//推
        SeatedRow,//拉
        TricepsPress,//下压
        LateralPull,//高拉
        LegPress,//下肢蹬踏
        Pulley,
    }

    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class LarkSetting : Page
    {
        enum LarkMode
        {
            IDEL = 2,
            IsoTonic,
            IsoKinetic,
            IsoMetric,
            PositonSearch,
        }
        LarkMode mode;
        int angle = 0;
        float startStopAngle = 355;
        float endStopAngle = 95;
        int startPos = 6;
        int endPos = 86;
        float Length = 0;
        float Force = 0;
        float Speed = 0;
        float ConSpeed = 10;
        float EccSpeed = 10;
        float ConForce = 45;
        float EccForce = 45;// n.M
        bool dir = true;
        bool oldDir = true;
        int count = 0;
        int errorCode = 0;
        int conDir = 0;
        public LarkSetting()
        {
            this.InitializeComponent();
            sliderArm.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            sliderSeat.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            eccSpeedSlider.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            conSpeedSlider.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            forceSlider.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            bool stopCalibration = true;
            serchComm("EQTYPE", (int type) =>
            {
                string deviceName = "膝关节伸展测训单元";
                switch ((DeviceType)type)
                {
                    case DeviceType.LegExtension: deviceName = "膝关节伸展测训单元"; break;
                    case DeviceType.LegCurl: deviceName = "膝关节屈曲测训单元"; break;
                    case DeviceType.Abdominal: deviceName = "腹肌测训单元"; break;
                    case DeviceType.BackExtension: deviceName = "背肌测训单元"; break;
                    case DeviceType.ChestPress: deviceName = "胸推测训单元"; break;
                    case DeviceType.SeatedRow: deviceName = "坐式划船测训单元"; break;
                    case DeviceType.TricepsPress: deviceName = "下压复合测训单元"; break;
                    case DeviceType.LateralPull: deviceName = "高拉复合测训单元"; break;
                    case DeviceType.LegPress: deviceName = "下肢蹬踏测训单元"; break;
                    default: break;
                }
                MainWindow.Current.invoke(() =>
                {
                    DeviceName.Text = deviceName;
                });
            });
            serchComm("CONDIR", (int dir) =>
            {
                conDir = dir;
            });
            SerialCommRegex.Current.register("Lark", new ExecComm
            {
                regex = "\\[<POS:([-.0-9]+)><FORCE:([-.0-9]+)><SPEED:([-.0-9]+)><DIR:([0-9])><ERRCODE:([-.0-9]+)>..]",
                action = (string item, Match match) =>
                {
                    angle = int.Parse(match.Groups[1].Value);
                    Force = float.Parse(match.Groups[2].Value);
                    Speed = float.Parse(match.Groups[3].Value);
                    dir = int.Parse(match.Groups[4].Value) == 1 ? true : false;
                    if (dir != oldDir)
                    {
                        count++;
                    }
                    oldDir = dir;
                    if (stopCalibration)
                    {
                        if (dir)
                        {
                            MainWindow.Current.invoke(() =>
                            {
                                serchComm("ENDSTOP:1", "<ENDSTOP:1><POS:([-.0-9]+)>", (int a) =>
                                {
                                    startStopAngle = a;
                                    startPos = a + 5;
                                });
                                serchComm("ENDSTOP:2", "<ENDSTOP:2><POS:([-.0-9]+)>", (int a) =>
                                {
                                    endStopAngle = a;
                                    endPos = a - 5;
                                });
                            });
                            stopCalibration = false;
                        }
                    }
                    errorCode = int.Parse(match.Groups[5].Value);
                    canvas.Invalidate();
                    MainWindow.Current.invoke(() =>
                    {
                        switch (mode)
                        {
                            case LarkMode.PositonSearch:
                                if (angle < startPos || startPos == -1)
                                {
                                    startPos = angle;
                                }
                                if (angle > endPos || endPos == -1)
                                {
                                    endPos = angle;
                                }
                                if (conDir == 1)
                                {
                                    if (endPos > startPos)
                                    {
                                        StartPosText.Text = $"{startPos:N}°";
                                        EndPosText.Text = $"{endPos:N}°";
                                        CmdSend($"STARTPOS:{startPos:N0}><ENDPOS:{endPos:N0}");
                                    }
                                    else
                                    {
                                        StartPosText.Text = $"{endPos:N}°";
                                        EndPosText.Text = $"{startPos:N}°";
                                        CmdSend($"STARTPOS:{endPos:N0}><ENDPOS:{startPos:N0}");
                                    }
                                }
                                else if (conDir == -1)
                                {
                                    if (endPos < startPos)
                                    {
                                        StartPosText.Text = $"{startPos:N}°";
                                        EndPosText.Text = $"{endPos:N}°";
                                        CmdSend($"STARTPOS:{startPos:N0}><ENDPOS:{endPos:N0}");
                                    }
                                    else
                                    {
                                        StartPosText.Text = $"{endPos:N}°";
                                        EndPosText.Text = $"{startPos:N}°";
                                        CmdSend($"STARTPOS:{endPos:N0}><ENDPOS:{startPos:N0}");
                                    }
                                }
                                break;
                            default:
                                break;
                        }
                        RunCount.Text = $"{count / 2:N0}";
                        SpeedText.Text = $" {Speed:N0}°/s";
                        ForceText.Text = $" {(Force / ((303 + (Length / 50) * (455 - 303)) / 1000)) / 9.8f:F2} KG";
                    });
                    return false;
                }
            });
        }
        public struct DeviceInfo
        {
            int type;
            string name;
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            CmdSend((string)(sender as Button).Tag);
        }
        private void serchComm(string cmd, string regex, Action<int> action)
        {
            SerialCommRegex.Current.register(cmd, new ExecComm
            {
                regex = regex,
                action = (string item, Match match) =>
                {
                    action(int.Parse(match.Groups[1].Value));
                    return true;
                }
            });
            CmdSend(cmd);
        }
        private void serchComm(string cmd, Action<int> action)
        {
            string reg = cmd;
            if (cmd.IndexOf(':') >= 0)
                reg = cmd.Substring(0, cmd.IndexOf(':'));
            SerialCommRegex.Current.register(cmd, new ExecComm
            {
                regex = reg + ":([0-9]+)>",
                action = (string item, Match match) =>
                {
                    action(int.Parse(match.Groups[1].Value));
                    return true;
                }
            });
            CmdSend(cmd);
        }
        private void canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
        {
            Vector2 center = new Vector2(0);
            CanvasSolidColorBrush brush = new CanvasSolidColorBrush(sender, Colors.Black);

            center.X = (float)(sender.ActualWidth / 2);
            center.Y = (float)(sender.ActualHeight / 2);
            Vector2 end = center;
            Vector2 stopStartPos = new Vector2();
            Vector2 stopEndPos = new Vector2();
            Vector2 StartPos = new Vector2();
            Vector2 EndPos = new Vector2();
            stopStartPos.X = end.X + (center.Y - 50) * MathF.Cos(MathF.PI / 180 * startStopAngle);
            stopStartPos.Y = end.Y + (center.Y - 50) * MathF.Sin(MathF.PI / 180 * startStopAngle);
            stopEndPos.X = end.X + (center.Y - 50) * MathF.Cos(MathF.PI / 180 * endStopAngle);
            stopEndPos.Y = end.Y + (center.Y - 50) * MathF.Sin(MathF.PI / 180 * endStopAngle);

            StartPos.X = end.X + (center.Y - 50) * MathF.Cos(MathF.PI / 180 * startPos);
            StartPos.Y = end.Y + (center.Y - 50) * MathF.Sin(MathF.PI / 180 * startPos);
            EndPos.X = end.X + (center.Y - 50) * MathF.Cos(MathF.PI / 180 * endPos);
            EndPos.Y = end.Y + (center.Y - 50) * MathF.Sin(MathF.PI / 180 * endPos);

            end.X += (center.Y - 115 + Length) * MathF.Cos(MathF.PI / 180 * angle);
            end.Y += (center.Y - 115 + Length) * MathF.Sin(MathF.PI / 180 * angle);
            var ds = args.DrawingSession;
            ds.DrawCircle(center, center.Y - 50, brush);
            ds.DrawLine(center, end, Colors.RosyBrown, 12);
            ds.FillCircle(center, 10, Colors.Red);
            ds.FillCircle(end, 10, Colors.Black);
            ds.FillCircle(StartPos, 10, Colors.Red);
            ds.FillCircle(EndPos, 10, Colors.Red);
            ds.FillCircle(stopStartPos, 10, Colors.Gray);
            ds.FillCircle(stopEndPos, 10, Colors.Gray);
            ds.DrawLine(center, StartPos, Colors.Red, 7);
            ds.DrawLine(center, EndPos, Colors.Red, 7);
        }

        private void Slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            switch ((string)(((Slider)sender).Tag))
            {
                case "Arm":
                    Length = (float)((e.NewValue - sliderArm.Minimum) / (sliderArm.Maximum - sliderArm.Minimum) * 50);
                    ArmLengthText.Text = $"运动臂长度  {e.NewValue:N0}mm";
                    canvas.Invalidate();
                    break;
                case "Seat":
                    SeatText.Text = $"座椅长度  {e.NewValue:N0}mm";
                    break;
                case "ECC":
                    EccSpeed = (float)e.NewValue;
                    EccSpeedText.Text = $"离心速度  {EccSpeed:N}°/s";
                    break;
                case "CON":
                    ConSpeed = (float)e.NewValue;
                    ConSpeedText.Text = $"向心速度  {ConSpeed:N}°/s";
                    break;
                case "FORCE":
                    EccForce = ConForce = (float)(e.NewValue * 9.8f * sliderArm.Value / 1000);
                    ConEccForceText.Text = $"机械负载 {e.NewValue:N0}KG";
                    break;
            }
        }
        private void Slider_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            switch (((sender as Slider).Tag as String))
            {
                case "Arm":
                    CmdSend($"ACTUATOR:0><POS:{(sender as Slider).Value:N}></ACTUATOR>");
                    break;
                case "Seat":
                    CmdSend($"ACTUATOR:1><POS:{(sender as Slider).Value:N}></ACTUATOR>");
                    break;
                case "ECC":
                    CmdSend($"ECCSPEED:{EccSpeed:N0}");
                    break;
                case "CON":
                    CmdSend($"CONSPEED:{ConSpeed:N0}");
                    break;
                case "FORCE":
                    CmdSend($"ECCFORCE:{EccForce:N0}");
                    CmdSend($"CONFORCE:{ConForce:N0}");
                    break;
            }
        }
        private void RadioButton_Checked(object sender, RoutedEventArgs e)
        {
            ModeText.Text = "运行模式 " + (sender as RadioButton).Content as string;
            int temp = int.Parse(((RadioButton)sender).Tag as String);
            string value = "MODE:" + temp;
            mode = (LarkMode)temp;
            count = 0;
            CmdSend(value);
        }

        private async void CmdSend(string value)
        {
            value = "<" + value + ">";
            byte[] bytes = Encoding.ASCII.GetBytes(value);
            byte sum = 0;
            foreach (byte b in bytes)
            {
                sum += b;
            }
            string cmd = $"[{value:s}{sum:X2}]";
            await HomePage.Current.serialWrite(cmd);
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            startPos = -1;
            endPos = -1;
            StartPosText.Text = "-1°";
            EndPosText.Text = "-1°";
            canvas.Invalidate();
        }

        private void Page_Unloaded(object sender, RoutedEventArgs e)
        {
            SerialCommRegex.Current.unregister("Lark");
        }
    }
}
